﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using IronLua.Metadata;
using IronLua.Runtime;

namespace IronLua
{
    public sealed class Lua
        : IDisposable
    {
 
        private readonly LuaEnvironment environ;
        private readonly Thread thread;
        private bool disposed;

        internal Lua(LuaEnvironment environ, Thread thread)
        {
            this.environ = environ;
            this.thread = thread;
        }

        public T Eval<T>(string chunk, params object[] args)
        {
            var bytes = Encoding.ASCII.GetBytes(chunk);
            return this.Eval<T>(bytes, args);
        }

        public T Eval<T>(byte[] chunk, params object[] args)
        {
            var result = this.Eval(chunk, args);
            try
            {
                return (T)Convert.ChangeType(result, typeof(T));
            }
            catch (Exception)
            {
                return (T)result;
            }
        }

        public object Eval(string chunk, params object[] args)
        {
            var bytes = Encoding.ASCII.GetBytes(chunk);
            return this.Eval(bytes, args);
        }

        public object Eval(byte[] chunk, params object[] args)
        {
            var stack = this.thread.Stack;
            var fn = Builtin.Functions.load;
            var fnindex = stack.Push(fn);
            stack.Push(Value.FromLightUserData(chunk));
            VirtualMachine.Call(
                thread: this.thread, 
                callable: fn, 
                callableStackIndex: fnindex, 
                actualParamCount: 1, 
                expectedResultCount: 1);

            stack.Push(ClrInterop.ToLuaValues(args).ToArray());
            fn = stack[fnindex];
            VirtualMachine.Call(
                this.thread, 
                callable: fn, 
                callableStackIndex: fnindex, 
                actualParamCount: args.Length,
                expectedResultCount: VirtualMachine.MultipleResults);
            IEnumerable<Value> results;

            if (!stack.TryPopUpTo(fnindex, out results))
            {
                return null;
            }
            var returnValues = results.ToArray();
            var retCount = returnValues.Length;

            if (retCount < 1)
            {
                return null;
            }
            else if (retCount == 1)
            {
                return returnValues[0].ToClrValue();
            }

            return ClrInterop.ToClrValues(returnValues);
        }

        void IDisposable.Dispose()
        {
            this.Dispose(disposing: true);
        }

        private void Dispose(bool disposing)
        {
            if (this.disposed)
            {
                throw new ObjectDisposedException(this.GetType().Name);
            }

            if (disposing)
            {

            }

            this.disposed = true;
        }

    }
}
